﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;

namespace EBA.IoC
{
    /// <summary>
    /// 容器配置文件
    /// </summary>
    public class ContainerConfiguration
    {
        internal AutowiredConventions AutowiredConventions { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        public ContainerConfiguration()
            : this(null)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public ContainerConfiguration(string configFile)
        {
            Configuration Config = AppRuntime.OpenConfiguration(configFile);

            this.AutowiredConventions = new AutowiredConventions();


            
        //    ConfigurationManager.RefreshSection("eba");

            var ebaSectionGroup = Config.GetSectionGroup("eba");

            if (ebaSectionGroup != null)
            {
                var containerSection = ebaSectionGroup.Sections.Get("container") as ContainerConfigurationSection;

                if (containerSection != null)
                {
                    this.ContainerType = containerSection.Type;

                    foreach (TypeConfigurationElement type in containerSection.Types)
                    {
                        this.WithPart(Type.GetType(type.Type));
                    }

                    foreach (AssemblyConfigurationElement assembly in containerSection.Assemblies)
                    {
                        var loaded = AppDomain.CurrentDomain.GetAssemblies().Where(i => i.FullName == assembly.Assembly || i.GetName().Name == assembly.Assembly).FirstOrDefault();

                        if (loaded == null)
                        {
                            loaded = Assembly.Load(assembly.Assembly);
                        }

                        this.WithAssembly(loaded);
                    }
                }
            }
        }

        string ContainerType { get; set; }

        public IContainer CreateContainer()
        {
            if (string.IsNullOrWhiteSpace(this.ContainerType))
            {
                return new SimpleContainer(this.AutowiredConventions);
            }
            else
            {
                var type = Type.GetType(this.ContainerType);
                var container = Activator.CreateInstance(type);

                return container as IContainer;
            }

        }

        public ContainerConfiguration WithAssemblies(IEnumerable<Assembly> assemblies)
        {
            foreach (var assembly in assemblies)
            {
                this.WithParts(assembly.TryGetTypes());
            }

            return this;

        }

        public ContainerConfiguration WithAssembly(Assembly assembly)
        {
            this.WithParts(assembly.TryGetTypes());
            return this;
        }

        public ContainerConfiguration WithPart<TPart>()
        {
            return WithPart(typeof(TPart));
        }

        public ContainerConfiguration WithPart(Type partType)
        {
            if (partType != null)
            {
                TryLoadExports(partType);

                TryLoadImports(partType);
            }

            return this;
        }



        public ContainerConfiguration WithParts(IEnumerable<Type> partTypes)
        {

            foreach (var partType in partTypes)
            {
                this.WithPart(partType);
            }

            return this;
        }

        public ContainerConfiguration WithParts(params Type[] partTypes)
        {

            if (partTypes != null)
            {
                foreach (var partType in partTypes)
                {
                    this.WithPart(partType);
                }
            }

            return this;

        }

        #region TryLoadImports

        static Dictionary<Type, ImportConvention> TYPE_IMPORT_CONVENTIONS = new Dictionary<Type, ImportConvention>();

        static volatile object TYPE_IMPORT_CONVENTIONS_SYNCROOT = new object();
        /// <summary>
        /// 分析类型元数据，提取Import信息
        /// </summary>
        /// <param name="partType"></param>
        private void TryLoadImports(Type partType)
        {
            if (TYPE_IMPORT_CONVENTIONS.ContainsKey(partType) == false)
            {
                lock (TYPE_IMPORT_CONVENTIONS_SYNCROOT)
                {
                    if (TYPE_IMPORT_CONVENTIONS.ContainsKey(partType) == false)
                    {
                        var convention = new ImportConvention(partType);

                        TYPE_IMPORT_CONVENTIONS.Add(partType, convention);
                    }

                }
            }

            this.AutowiredConventions.Imports[partType] = TYPE_IMPORT_CONVENTIONS[partType];
        }
        #endregion

        #region TryLoadExports
        /// <summary>
        /// 缓存类型的Export信息
        /// </summary>
        static Dictionary<Type, List<ExportConvention>> TYPE_EXPORT_CONVENTIONS = new Dictionary<Type, List<ExportConvention>>();

        static volatile object TYEP_EXPORT_CONVENTIONS_SYNCROOT = new object();
        /// <summary>
        /// 分析类型元数据，提取Export信息。
        /// </summary>
        /// <param name="partType"></param>
        private void TryLoadExports(Type partType)
        {
            if (TYPE_EXPORT_CONVENTIONS.ContainsKey(partType) == false)
            {
                lock (TYEP_EXPORT_CONVENTIONS_SYNCROOT)
                {
                    if (TYPE_EXPORT_CONVENTIONS.ContainsKey(partType) == false)
                    {
                        var items = new List<ExportConvention>();

                        foreach (var export in partType.GetCustomAttributes(typeof(ExportAttribute), true).OfType<ExportAttribute>())
                        {
                            items.Add(new ExportConvention(export.ContractType, export.ContractName, partType));
                        }

                        TYPE_EXPORT_CONVENTIONS.Add(partType, items);
                    }
                }

            }

            var conventions = TYPE_EXPORT_CONVENTIONS[partType];

            foreach (var convention in conventions)
            {
                this.AutowiredConventions.Exports.Add(convention);
            }


        }
        #endregion
    }
}
